package org.yelongframework.zip.impl.ant;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.yelongframework.commons.io.FileUtil;
import org.yelongframework.commons.io.FileNameUtil;
import org.yelongframework.lang.Nullable;
import org.yelongframework.zip.UnZipFileNameFuction;
import org.yelongframework.zip.ZipFileEntry;
import org.yelongframework.zip.ZipFileEntryDataSupplier;
import org.yelongframework.zip.ZipFileEntrys;
import org.yelongframework.zip.ZipFileException;

/**
 * 通过Apache的ant实现的压缩文件工具
 * 
 * @since 1.0.0
 */
public class DefaultAntZipFileTools implements AntZipFileTools {

	/** 默认编码 */
	public static final String DEFAULT_ENCODING = "GBK";

	/** 编码 */
	@Nullable
	private String encoding;

	public DefaultAntZipFileTools() {
	}

	public DefaultAntZipFileTools(@Nullable String encoding) {
		this.encoding = encoding;
	}

	@Override
	public void zip(ZipFileEntrys zipFileEntrys, File file) throws ZipFileException, IOException {
		try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
			zip(zipFileEntrys, fileOutputStream);
		}
	}

	@Override
	public void zip(ZipFileEntrys zipFileEntrys, OutputStream outputStream) throws ZipFileException, IOException {
		ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
		if (null != encoding) {
			zipOutputStream.setEncoding(encoding);
		}
		List<ZipFileEntry> entrys = zipFileEntrys.getEntries();
		for (ZipFileEntry entry : entrys) {
			if (entry.isDirectory()) {
				// 结尾以 / 视为文件夹
				zipOutputStream.putNextEntry(new ZipEntry(entry.getName() + "/"));
				continue;
			}
			zipOutputStream.putNextEntry(new ZipEntry(entry.getName()));
			ZipFileEntryDataSupplier dataSupplier = entry.getDataSupplier();
			byte[] data = dataSupplier.get();
			if (null != data) {
				zipOutputStream.write(data, 0, data.length);
			}
		}
		zipOutputStream.close();
	}

	@Override
	public void unzip(File zipFile, String unzipRootPath) throws ZipFileException, IOException {
		unzip(zipFile, x -> {
			if (StringUtils.isBlank(unzipRootPath)) {
				return x;
			}
			return FileNameUtil.getFilePath(unzipRootPath, x);
		});
	}

	@Override
	public void unzip(File zipFile, UnZipFileNameFuction unZipFileNameFuction) throws ZipFileException, IOException {
		ZipFileEntrys zipFileEntrys = unzip(zipFile);
		List<ZipFileEntry> entries = zipFileEntrys.getEntries();
		if (entries.isEmpty()) {
			return;
		}
		for (ZipFileEntry zipFileEntry : entries) {
			String name = zipFileEntry.getName();
			String fileName = unZipFileNameFuction.apply(name);
			if (zipFileEntry.isDirectory()) {
				FileUtil.createDirectory(fileName);
			} else {
				File file = FileUtil.createNewFileOverride(fileName);
				ZipFileEntryDataSupplier dataSupplier = zipFileEntry.getDataSupplier();
				byte[] data = dataSupplier.get();
				if (null != data) {
					FileUtils.writeByteArrayToFile(file, data);
				}
			}
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public ZipFileEntrys unzip(File zipFile) throws ZipFileException, IOException {
		ZipFile antZipFile = buildZipFile(zipFile);
		Enumeration<ZipEntry> entries = antZipFile.getEntries();
		ZipFileEntrys zipFileEntrys = new ZipFileEntrys();
		while (entries.hasMoreElements()) {
			ZipEntry zipEntry = entries.nextElement();
			String name = zipEntry.getName();
			ZipFileEntry zipFileEntry = null;
			if (zipEntry.isDirectory()) {
				zipFileEntry = new ZipFileEntry(name).setFileTypeDirectory();
			} else {
				InputStream inputStream = antZipFile.getInputStream(zipEntry);
				byte[] data = IOUtils.toByteArray(inputStream);
				inputStream.close();
				zipFileEntry = new ZipFileEntry(name, () -> data).setFileTypeFile();
			}
			zipFileEntrys.add(zipFileEntry);
		}
		ZipFile.closeQuietly(antZipFile);
		return zipFileEntrys;
	}

	/**
	 * 构建压缩文件实例
	 * 
	 * @param zipFile 压缩文件
	 * @return 压缩文件实例
	 * @throws IOException 流异常
	 */
	protected ZipFile buildZipFile(File zipFile) throws IOException {
		ZipFile antZipFile;
		if (null == encoding) {
			antZipFile = new ZipFile(zipFile);
		} else {
			antZipFile = new ZipFile(zipFile, encoding);
		}
		return antZipFile;
	}

}
